package com.agynamix.scala.mi

import _root_.java.io._
import _root_.java.lang._
import _root_.java.util.Properties
import _root_.javax.script.{ScriptEngineManager, ScriptException}
import Conversions._

/**
 * Created by IntelliJ IDEA.
 * User: tuhlmann
 * Date: 03.04.2010
 * Time: 19:34:48
 * To change this template use File | Settings | File Templates.
 */

object SynchronizeFolderTree {

  val RC_OK = 0
  val RC_ERROR = 1

  val mgr = new ScriptEngineManager()
  val scriptEngine = mgr.getEngineByName("AppleScript")  

  def main(args:Array[String]) {
    println("Cleanup dormant file entries")

    val entriesToRemove = cleanupSyncedFilesList()
    removeEntriesFromITunes(entriesToRemove)

    var rcs = List(RC_OK)
    
    val props = new Properties()
    props.load(this.getClass.getClassLoader.getResourceAsStream("mediaimport.properties"))

    rcs = rcs ::: readMultiplePropertyEntries(props, "moviePath").map(directory => {
      println("Synchronize Movie Folder "+directory);
      SynchronizeFolderTree(directory  , new MovieFileProcessor())
    })

    rcs = rcs ::: readMultiplePropertyEntries(props, "tvSeriesPath").map(directory => {
      println("Synchronize TV Show Folder "+directory);
      SynchronizeFolderTree(directory  , new TVSeriesFileProcessor())
    })

    rcs = rcs ::: readMultiplePropertyEntries(props, "itunesShowsPath").map(directory => {
      println("Synchronize iTunes Show Folder "+directory)
      SynchronizeFolderTree(directory  , new ITunesMediaProcessor())
    })

    println("RC Size: "+rcs.size)
    val rc = rcs.reduceLeft((op1: Int, op2: Int) => op1 max op2)

    println("RC = "+rc)
    System.exit(rc)

  }

  def apply(directory: String, mediaFileProcessor: MediaFileProcessor): Int = {
    if (new File(directory).exists())
    {
      try {
        new SynchronizeFolderTree(directory, mediaFileProcessor).run()
      } catch {
        case _ => return RC_ERROR
      }
      RC_OK
    } else {
      println("Directory "+directory+" does not exist.")
      RC_ERROR
    }
  }

  def getSynchronizedFilesList(): List[MediaLogEntry] = {

    var syncedFiles: List[MediaLogEntry]  = Nil

    try
    {
      // Deserialize from a file
      val file = new File("syncedFiles.ser");
      val in = new ObjectInputStream(new FileInputStream(file))
      // Deserialize the object
      syncedFiles = in.readObject().asInstanceOf[List[MediaLogEntry]]
      in.close()
    } catch {
      case _ =>
    }
    return syncedFiles;
  }

  def writeSyncedFilesList(syncedFiles: List[MediaLogEntry])
  {
    try {
        // Serialize to a file
        val out = new ObjectOutputStream(new FileOutputStream("syncedFiles.ser"));
        out.writeObject(syncedFiles);
        out.close();
    } catch {
      case e: IOException =>
    }
  }

  def cleanupSyncedFilesList(): List[MediaLogEntry] = {

    val syncedFilesList: List[MediaLogEntry] = getSynchronizedFilesList()

    val cleanedUpList = syncedFilesList.filterNot((logEntry: MediaLogEntry) => {
      if (!logEntry.file.exists) {
        true
      } else {
        false
      }
    })

    writeSyncedFilesList(cleanedUpList)

    return syncedFilesList filterNot (cleanedUpList contains)
  }

  def removeEntriesFromITunes(entriesToRemove: List[MediaLogEntry]): Unit = {
    entriesToRemove.foreach(entry => {
      println("Remove "+entry.file+" from file list and iTunes")
      if ((entry.permanentId != null) && (entry.permanentId.length > 0))
      {
        removeFromITunes(entry)
      }
    })

  }

  def removeFromITunes(logEntry: MediaLogEntry): Unit = {
    try {
      val sb = new StringBuilder()
      sb.append("tell application \"iTunes\"\n")
      sb.append("delete (get some track of library playlist 1 whose persistent ID is \""+logEntry.permanentId+"\")\n")
      sb.append("end tell")

      scriptEngine.eval(sb.toString())
    } catch {
      case e => println("Could not remove file "+logEntry.file+" ("+logEntry.permanentId+") from iTunes. Error: "+e.getMessage())
    }
  }

  def readMultiplePropertyEntries(props: Properties, keyPrefix: String): List[String] = {
    var re = List[String]()

    for (key <- props.keySet.iterator) {
      if (key.toString.startsWith(keyPrefix)) {
        re = props.get(key).toString :: re
      }

    }

    return re
  }

}

class SynchronizeFolderTree(mediaDirectory: String, mediaFileProcessor: MediaFileProcessor) {

  def run() {
    var count = 0;

    var syncedFiles = SynchronizeFolderTree.getSynchronizedFilesList();

    val filter = (file: File) => {
      (!file.getName.startsWith(".")) && (file.getName.endsWith(".m4v"))
    }

    try {

      new File(mediaDirectory).traverse((file: File) => filter(file),
        (file: File) =>
          if (!syncedFiles.exists(entry => entry.file == file))
          {
            try {
              val result = mediaFileProcessor.process(file)
              if (result.processSuccessful)
              {
                syncedFiles = MediaLogEntry(file, result.permanentId) :: syncedFiles
                count += 1;
                //println("ID: "+result.permanentId+", File: "+file)
              }
            } catch {
              case e: ScriptException =>
                println("Error adding file "+file+" to iTunes: "+e.getMessage());
            }
          }
         )

    } catch {
      case e => println("There was an error running SynchronizeFolderTree: "+e.getMessage)
      e.printStackTrace()
    }
    println("Processed " + count + " files in "+mediaDirectory)
    SynchronizeFolderTree.writeSyncedFilesList(syncedFiles)
  }
}

